﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;

namespace XNAProjectLibrary
{
    public interface IInputHandler                                                          //interface declaration
    {
        bool WasPressed(int playerIndex, Buttons button, Keys keys);                        //specifications
        bool MouseRightClicked();
        bool MouseLeftClicked();

        KeyboardHandler KeyboardState { get; }

        GamePadState[] GamePads { get; }

        ButtonHandler ButtonHandler { get; }

#if !XBOX360
        MouseState MouseState { get; }
        MouseState PreviousMouseState { get; }
        Vector2 MousePosition { get; }
        Ray CursorRay(Matrix projectionMatrix, Matrix viewMatrix);
        Vector3 CursorDirection(Matrix projectionMatrix, Matrix viewMatrix);
        Vector3 NearPoint(Matrix projectionMatrix, Matrix viewMatrix);
        Vector3 FarPoint(Matrix projectionMatrix, Matrix viewMatrix);
        Vector3 NearSource { get; }
        Vector3 FarSource { get; }
#endif
    };

    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class InputHandler : Microsoft.Xna.Framework.GameComponent, IInputHandler
    {
        private KeyboardHandler keyboard;                                                   //keyboard
        private ButtonHandler gamePadHandler = new ButtonHandler();                         //gamepad button handler

#if !XBOX360
        private MouseState mouseState;                                                      //mouse state handler
        private MouseState prevMouseState;                                                  //need two so we can compare between actions

#endif

        private bool allowsExiting;                                                         //speaks for itself

        public InputHandler(Game game) : this(game, false) { }                              //constructor
        public InputHandler(Game game, bool allowsExiting)                                  //constructor
            : base(game)
        {
            this.allowsExiting = allowsExiting;                                             //init exiting sht

            game.Services.AddService(typeof(IInputHandler), this);                          //add this class to services
                                                                                            //becomes "GLOBAL"
            keyboard = new KeyboardHandler();                                               //initialize keyboard

#if !XBOX360
            Game.IsMouseVisible = true;                                                     //make mouse visible
            prevMouseState = Mouse.GetState();                                              //set up first mouse state
#endif
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            keyboard.Update();                                                              //update keyboard
            gamePadHandler.Update();                                                        //update gamepad

            if (allowsExiting)                                                              //exit if needed
            {
                if (keyboard.IsKeyDown(Keys.Escape))
                    Game.Exit();

                // Allows the default game to exit on Xbox 360 and Windows
                if (gamePadHandler.WasButtonPressed(0, Buttons.Back))
                    Game.Exit();
            }

#if !XBOX360

            prevMouseState = mouseState;                                                    //progress in time
            mouseState = Mouse.GetState();                                                  //get new state

#endif

            base.Update(gameTime);                                                          //base class update
        }

        #region IInputHandler Members

        public Vector3 NearPoint(Matrix projectionMatrix, Matrix viewMatrix)
        {
            return Game.GraphicsDevice.Viewport.Unproject(this.NearSource,
                projectionMatrix, viewMatrix, Matrix.Identity);
        }

        public Vector3 FarPoint(Matrix projectionMatrix, Matrix viewMatrix)
        {
            return Game.GraphicsDevice.Viewport.Unproject(this.FarSource,
                projectionMatrix, viewMatrix, Matrix.Identity);
        }

        public Vector3 CursorDirection(Matrix projectionMatrix, Matrix viewMatrix)
        {
            Vector3 cursorDirection = FarPoint(projectionMatrix, viewMatrix) - NearPoint(projectionMatrix, viewMatrix);
            cursorDirection.Normalize();
            return cursorDirection;
        }

        public Vector3 NearSource
        {
            get
            {
                Vector3 nearSource = new Vector3(mouseState.X, mouseState.Y, 0f);
                return nearSource;
            }
        }
        public Vector3 FarSource 
        { 
            get 
            {
                Vector3 farSource = new Vector3(mouseState.X, mouseState.Y, 1f);  
                return farSource; 
            } 
        }

        public bool MouseLeftClicked()
        {
            if ((mouseState.LeftButton == ButtonState.Pressed) && (prevMouseState.LeftButton == ButtonState.Released))
                return true;
            else
                return false;
        }

        public bool MouseRightClicked()
        {
            if ((mouseState.RightButton == ButtonState.Pressed) && (prevMouseState.RightButton == ButtonState.Released))
                return true;
            else
                return false;
        }

        public bool WasPressed(int playerIndex, Buttons button, Keys keys)
        {
            if (keyboard.WasKeyPressed(keys) || gamePadHandler.WasButtonPressed(playerIndex, button))
                return (true);
            else
                return (false);
        }

        public KeyboardHandler KeyboardState
        {
            get { return (keyboard); }
        }

        public ButtonHandler ButtonHandler
        {
            get { return (gamePadHandler); }
        }

        public GamePadState[] GamePads
        {
            get { return (gamePadHandler.GamePads); }
        }

#if !XBOX360

        public MouseState MouseState
        {
            get { return (mouseState); }
        }

        public MouseState PreviousMouseState
        {
            get { return (prevMouseState); }
        }

        public Vector2 MousePosition
        {
            get { return new Vector2(mouseState.X, mouseState.Y); }
        }

        public Ray CursorRay(Matrix projectionMatrix, Matrix viewMatrix)
        {
            Vector3 nearSource = new Vector3(mouseState.X, mouseState.Y, 0f);
            Vector3 farSource = new Vector3(mouseState.X, mouseState.Y, 1f);

            Vector3 nearPoint = Game.GraphicsDevice.Viewport.Unproject(nearSource,
                projectionMatrix, viewMatrix, Matrix.Identity);
            Vector3 farPoint = Game.GraphicsDevice.Viewport.Unproject(farSource,
                projectionMatrix, viewMatrix, Matrix.Identity);

            Vector3 cursorDirection = farPoint - nearPoint;
            cursorDirection.Normalize();

            Ray ray = new Ray(nearPoint, cursorDirection);

            return ray;
        }
        
#endif

        #endregion

    }



    public class ButtonHandler
    {
        private GamePadState[] prevGamePadsState = new GamePadState[4];                     //gamepadstate bullshit
        private GamePadState[] gamePadsState = new GamePadState[4];


        public GamePadState[] GamePads                                                      //getter
        {
            get
            {
                return (gamePadsState);
            }
        }

        public ButtonHandler()                                                              //button handler still gamepad
        {
            prevGamePadsState[0] = GamePad.GetState(PlayerIndex.One);
            prevGamePadsState[1] = GamePad.GetState(PlayerIndex.Two);
            prevGamePadsState[2] = GamePad.GetState(PlayerIndex.Three);
            prevGamePadsState[3] = GamePad.GetState(PlayerIndex.Four);
        }

        public void Update()                                                                //gamepad update bullshit
        {
            //set our previous state to our new state
            prevGamePadsState[0] = gamePadsState[0];
            prevGamePadsState[1] = gamePadsState[1];
            prevGamePadsState[2] = gamePadsState[2];
            prevGamePadsState[3] = gamePadsState[3];

            //get our new state
            //gamePadsState = GamePad.State .GetState();
            gamePadsState[0] = GamePad.GetState(PlayerIndex.One);
            gamePadsState[1] = GamePad.GetState(PlayerIndex.Two);
            gamePadsState[2] = GamePad.GetState(PlayerIndex.Three);
            gamePadsState[3] = GamePad.GetState(PlayerIndex.Four);
        }

        public bool WasButtonPressed(int playerIndex, Buttons button)                       //gamepad bullshit
        {
            return (gamePadsState[playerIndex].IsButtonDown(button) &&
                prevGamePadsState[playerIndex].IsButtonUp(button));
        }
    }

    public class KeyboardHandler                                                            //here we go keyboard handler
    {
        private KeyboardState prevKeyboardState;                                            //need two states like mouse
        private KeyboardState keyboardState;                                                //for comparing

        public KeyboardHandler()                                                            //constructor
        {
            prevKeyboardState = Keyboard.GetState();                                        //set prevstate like mouse
        }

        public Keys[] GetPressedKeys()
        {
            return keyboardState.GetPressedKeys();
        }

        public bool IsKeyDown(Keys key)                                                     //simple iskeydown
        {
            return (keyboardState.IsKeyDown(key));
        }

        public bool IsKeyUp(Keys key)                                                     //simple iskeydown
        {
            return (keyboardState.IsKeyUp(key));
        }

        public bool IsHoldingKey(Keys key)                                                  //simple isholdingkey
        {
            return (keyboardState.IsKeyDown(key) &&                                         //if prev and this is the same key then holding
                prevKeyboardState.IsKeyDown(key));
        }

        public bool WasKeyPressed(Keys key)                                                 //simple waspressed
        {
            return (keyboardState.IsKeyDown(key) &&                                         //if prev is keydown
                prevKeyboardState.IsKeyUp(key));                                            //now is keyup then pressed
        }

        public bool HasReleasedKey(Keys key)                                                //simple hasreleased
        {
            return (keyboardState.IsKeyUp(key) &&                                           //if prev is down now is up then released
                prevKeyboardState.IsKeyDown(key));
        }

        public void Update()
        {
            prevKeyboardState = keyboardState;                                              //progress in time old=new

            keyboardState = Keyboard.GetState();                                            //get new keyboardstate
        }
    }
}